/*
 * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.xuexiang.xui_lib.component.button.shinebutton;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;

import java.lang.ref.WeakReference;
import java.util.Random;

import static com.xuexiang.xui_lib.component.button.shinebutton.PorterImageView.LABEL;

/**
 * ShineView
 *
 * @author Chad
 * @title com.sackcentury.shinebuttonlib
 * @description
 * @modifier
 * @date
 * @since 2016-07-05
 **/
public class ShineView extends Component implements Component.DrawTask {
    /**
     * default 10ms ,change to 25ms for saving cpu
     */
    private static final int FRAME_REFRESH_DELAY = 25;
    private static int[] sColorRandom = new int[10];
    private ShineAnimator mShineAnimator;
    private AnimatorValue mClickAnimator;

    private WeakReference<ShineButton> mShineButton;
    private Paint mPaint;
    private Paint mPaint2;
    private Paint mPaint3;
    private Paint mPaintSmall;

    private int mColorCount = 10;
    private float tempNum = 0;
    private float tempNum2 = 0.85f;

    private int mShineCount;
    private float mSmallOffsetAngle;
    private float mTurnAngle;
    private long mAnimDuration;
    private long mClickAnimDuration;
    private float mShineDistanceMultiple;
    private int mSmallShineColor;
    private int mBigShineColor;

    private int mShineSize = 0;

    private boolean mAllowRandomColor = false;
    private boolean mEnableFlashing = false;

    private RectFloat mRectF = new RectFloat();
    private RectFloat mSmallRectF = new RectFloat();

    private Random mRandom = new Random();
    private int mCenterAnimX;
    private int mCenterAnimY;
    private int fixdCenterAnimX;
    private int fixdCenterAnimY;

    private int mBtnWidth;
    private int mBtnHeight;

    private float mValue;
    private float mClickValue = 0;
    private boolean mIsAnimating = false;
    private float mDistanceOffset = 0.01f;

    /**
     * ShineView
     *
     * @param context
     */
    public ShineView(Context context) {
        super(context);
    }

    /**
     * ShineView
     *
     * @param context
     * @param shineButton
     * @param shineParams
     */
    public ShineView(Context context, final ShineButton shineButton, ShineParams shineParams) {
        super(context);
        initShineParams(shineParams, shineButton);
        mShineButton = new WeakReference<>(shineButton);
        mShineAnimator = new ShineAnimator(mAnimDuration, mShineDistanceMultiple, mClickAnimDuration);
        mPaint = new Paint();
        mPaint.setColor(new Color(mBigShineColor));
        mPaint.setStrokeWidth(10);
        mPaint.setStyle(Paint.Style.STROKE_STYLE);
        mPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);

        mPaint2 = new Paint();
        mPaint2.setColor(Color.WHITE);
        mPaint2.setStrokeWidth(10);
        mPaint2.setStrokeCap(Paint.StrokeCap.ROUND_CAP);

        mPaint3 = new Paint();
        mPaint3.setColor(new Color(mBigShineColor));

        // 画笔属性是实心圆
        mPaint3.setStyle(Paint.Style.FILL_STYLE);

        // 设置画笔粗细
        mPaint3.setStrokeWidth(4);
        mPaintSmall = new Paint();
        mPaintSmall.setColor(new Color(mSmallShineColor));
        mPaintSmall.setStrokeWidth(5);
        mPaintSmall.setStyle(Paint.Style.STROKE_STYLE);
        mPaintSmall.setStrokeCap(Paint.StrokeCap.ROUND_CAP);

        mClickAnimator = new AnimatorValue();
        mClickAnimator.setDelay(FRAME_REFRESH_DELAY);
        mClickAnimator.setDuration(mClickAnimDuration);

        mClickAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                mClickValue = value;
            }
        });

        mClickAnimator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                mClickValue = 0;
                invalidate();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });

        mShineAnimator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                if (mShineButton != null) {
                    mShineButton.get().removeRootView();
                }
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        addDrawTask(this);
    }

    /**
     * ShineView
     *
     * @param context
     * @param attrs
     */
    public ShineView(Context context, AttrSet attrs) {
        super(context, attrs);
    }

    /**
     * ShineView
     *
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public ShineView(Context context, AttrSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * showAnimation
     *
     * @param shineButton
     * @param canvas
     */
    public void showAnimation(ShineButton shineButton, Canvas canvas) {
        if (shineButton == null) {
            return;
        }
        mBtnWidth = shineButton.getWidth();
        mBtnHeight = shineButton.getHeight();
        float[] positions = shineButton.getContentPosition();
        mCenterAnimX = (int) positions[0] + shineButton.getWidth() / 2;
        mCenterAnimY = (int) positions[1] + shineButton.getHeight() / 2;

        fixdCenterAnimX = (int) positions[0] + shineButton.getWidth() / 2;
        fixdCenterAnimY = (int) positions[1] + shineButton.getHeight() / 2;

        if (shineButton.getWindowViewGroup() != null) {
            Component decor = shineButton.getWindowViewGroup();
            mCenterAnimX = mCenterAnimX - decor.getPaddingLeft();
            mCenterAnimY = mCenterAnimY - decor.getPaddingTop();
        }
        mShineAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                if (value < 0.5) {
                    return;
                }

                if (value > tempNum2) {
                    mShineDistanceMultiple = mShineDistanceMultiple - ((mShineDistanceMultiple - 1)
                        / (1 - tempNum2)) * (value - tempNum2);
                    if (value == 1) {
                        mShineDistanceMultiple = 1.0f;
                    }
                }
                mValue = value;

                if (mShineSize != 0 && mShineSize > 0) {
                    mPaint.setStrokeWidth(((float) mShineSize / 10 * 5) * (mShineDistanceMultiple - mValue));
                    mPaintSmall.setStrokeWidth(((float) mShineSize / 10 * 2) * (mShineDistanceMultiple - mValue));
                } else {
                    mPaint.setStrokeWidth((mBtnWidth / 8F) * (mShineDistanceMultiple - mValue));
                    mPaintSmall.setStrokeWidth((mBtnWidth / 14F) * (mShineDistanceMultiple - mValue));
                }
                mRectF.fuse(mCenterAnimX - (mBtnWidth / (3 - mShineDistanceMultiple) * mValue),
                    mCenterAnimY - (mBtnHeight / (3 - mShineDistanceMultiple) * mValue),
                    mCenterAnimX + (mBtnWidth / (3 - mShineDistanceMultiple) * mValue),
                    mCenterAnimY + (mBtnHeight / (3 - mShineDistanceMultiple) * mValue));
                mSmallRectF.fuse(mCenterAnimX - (mBtnWidth / ((3.15f - mShineDistanceMultiple)
                        + mDistanceOffset) * mValue),
                    mCenterAnimY - (mBtnHeight / ((3.15f - mShineDistanceMultiple) + mDistanceOffset) * mValue),
                    mCenterAnimX + (mBtnWidth / ((3.15f - mShineDistanceMultiple) + mDistanceOffset) * mValue),
                    mCenterAnimY + (mBtnHeight / ((3.15f - mShineDistanceMultiple) + mDistanceOffset) * mValue));
                invalidate();
            }
        });
        mShineAnimator.start();
        mClickAnimator.start();
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        for (int i = 0; i < mShineCount; i++) {
            if (mAllowRandomColor) {
                mPaint.setColor(new Color(sColorRandom[Math.abs(mColorCount / 2 - i)
                    >= mColorCount ? mColorCount - 1 : Math.abs(mColorCount / 2 - i)]));
            }
            float value = 360f / mShineCount * i + 1 + ((mValue - 1) * mTurnAngle);
            Arc arc = new Arc(value, 0.1f, false);
            canvas.drawArc(mRectF, arc, getConfigPaint(mPaint));
        }
        for (int i = 0; i < mShineCount; i++) {
            if (mAllowRandomColor) {
                mPaint.setColor(new Color(sColorRandom[Math.abs(mColorCount / 2 - i)
                    >= mColorCount ? mColorCount - 1 : Math.abs(mColorCount / 2 - i)]));
            }
            float value = 360f / mShineCount * i + 1 - mSmallOffsetAngle + ((mValue - 1) * mTurnAngle);
            Arc arc = new Arc(value, 0.1f, false);
            canvas.drawArc(mSmallRectF, arc, getConfigPaint(mPaintSmall));
        }
        mPaint.setStrokeWidth(mBtnWidth * (mClickValue) * (mShineDistanceMultiple - mDistanceOffset));
        HiLog.info(LABEL, "onDrawLog===>" + mClickValue);
        tempNum = tempNum + 1.0f;

        if (mClickValue != 0) {
            mPaint2.setStrokeWidth(mBtnWidth * (mClickValue) * (mShineDistanceMultiple - mDistanceOffset) - 8);
        } else {
            mPaint2.setStrokeWidth(0);
        }
        canvas.drawPoint(mCenterAnimX, mCenterAnimY, mPaint);
        canvas.drawPoint(mCenterAnimX, mCenterAnimY, mPaint2);
        HiLog.info(LABEL, "mCenterAnimX===>" + mCenterAnimX + ":" + mCenterAnimY);

        if (tempNum > 2.0 && tempNum < 6) {
            canvas.drawCircle(fixdCenterAnimX, fixdCenterAnimY, 75, mPaint3);
        }
        if (mShineAnimator != null && !mIsAnimating) {
            mIsAnimating = true;
            showAnimation(mShineButton.get(), canvas);
        }
    }

    private Paint getConfigPaint(Paint paint) {
        if (mEnableFlashing) {
            paint.setColor(new Color(sColorRandom[mRandom.nextInt(mColorCount - 1)]));
        }
        return paint;
    }

    /**
     * ShineParams
     *
     * @since 2021-04-26
     */
    public static class ShineParams {
        /**
         * allowRandomColor
         */
        public boolean allowRandomColor = false;
        /**
         * animDuration
         */
        public int animDuration = 1700;
        /**
         * bigShineColor
         */
        public int bigShineColor = 0;
        /**
         * clickAnimDuration
         */
        public int clickAnimDuration = 200;
        /**
         * enableFlashing
         */
        public boolean enableFlashing = false;
        /**
         * shineCount
         */
        public int shineCount = 7;
        /**
         * shineTurnAngle
         */
        public float shineTurnAngle = 20;
        /**
         * shineDistanceMultiple
         */
        public float shineDistanceMultiple = 1.9f;

        /**
         * smallShineOffsetAngle
         */
        public float smallShineOffsetAngle = 20;
        /**
         * smallShineColor
         */
        public int smallShineColor = 0;
        /**
         * shineSize
         */
        public int shineSize = 0;

        ShineParams() {
            sColorRandom[0] = Color.getIntColor("#FFFF99");
            sColorRandom[1] = Color.getIntColor("#FFCCCC");
            sColorRandom[2] = Color.getIntColor("#996699");
            sColorRandom[3] = Color.getIntColor("#FF6666");
            sColorRandom[4] = Color.getIntColor("#FFFF66");
            sColorRandom[5] = Color.getIntColor("#F44336");
            sColorRandom[6] = Color.getIntColor("#666666");
            sColorRandom[7] = Color.getIntColor("#CCCC00");
            sColorRandom[8] = Color.getIntColor("#666666");
            sColorRandom[9] = Color.getIntColor("#999933");
        }
    }

    private void initShineParams(ShineParams shineParams, ShineButton shineButton) {
        mShineCount = shineParams.shineCount;
        mTurnAngle = shineParams.shineTurnAngle;
        mSmallOffsetAngle = shineParams.smallShineOffsetAngle;
        mEnableFlashing = shineParams.enableFlashing;
        mAllowRandomColor = shineParams.allowRandomColor;
        mShineDistanceMultiple = shineParams.shineDistanceMultiple;
        mAnimDuration = shineParams.animDuration;
        mClickAnimDuration = shineParams.clickAnimDuration;
        mSmallShineColor = shineParams.smallShineColor;
        mShineSize = shineParams.shineSize;
        if (mSmallShineColor == 0) {
            mSmallShineColor = sColorRandom[6];
        }

        if (mBigShineColor == 0) {
            mBigShineColor = shineButton.getColor();
        }
    }
}
